import type { Router, RouteRecordRaw } from "vue-router";
import { useTimeoutFn } from "@vueuse/core";
import { ElMessage } from "element-plus";
import { cloneDeep } from "es-toolkit";
import { toRaw } from "vue";
import { authApi } from "@/api";
import { asyncPermissionStorage } from "@/config/storage";
import IFrame from "@/layout/Frame.vue";
import { useNav } from "@/layout/hooks/useNav";
import { $t } from "@/plugins/i18n";
import { useConfigStore } from "@/stores/config";
import { usePermissionStore } from "@/stores/permission";
import { buildHierarchyTree } from "@/utils/tools";
import router, { initConstantRoutes, originRoutes } from "./index";

// https://cn.vitejs.dev/guide/features.html#glob-import
const modulesRoutes = import.meta.glob("/src/views/**/*.{vue,tsx}");

// #region----------------------------------------路由工具-------------------------------------------

/** 判断路由是否有rank，首页路由返回false，因为它是默认的第一个路由 */
function hasRank(routeInfo: RouteRecordRaw): boolean {
    const { name, path, meta } = routeInfo;
    // 只判断顶级路由，二级路由不判断由后端排序
    // (meta?.rank === 0 && name !== "Home" && path !== "/") 首页判断，约定首页路由的rank为 0，name为 "Home"，path为 "/"
    return meta?.parentId == null
        ? (!!(meta?.rank == null || (meta?.rank === 0 && name !== "Home" && path !== "/")))
        : false;
}

/** 按照顶级路由中 meta 下的 rank 等级升序来排序路由，所有子路由不参与排序，交给后端排序，改变原数组 */
export function routeRankAscending(arr: any[]): RouteRecordRaw[] {
    arr.forEach((v, index) => {
        // 当rank不存在时, 根据顺序自动创建, 但都放在最后, 首页路由永远在第一位
        if (hasRank(v)) v.meta.rank = index + 10000;
    });
    return arr.sort((a, b) => a.meta.rank - b.meta.rank);
}

/** 动态title, 更新浏览器标签页标题 */
export function changeBrowserTitle(route: ToRouteType): void {
    const projectTitle = useNav().title.value; // 获取项目标题
    if (projectTitle) {
        document.title = `${$t(route.meta.title)} | ${projectTitle}`;
    } else {
        document.title = $t(route.meta.title);
    }
}

/**
 * 获取所有祖先路由包含自身, 返回顺序从父到子
 * 由于菜单路由被拍平, 不能直接通过 route.matched 获取
 * @param list 当前路由的 pathList
 * @param includeRoot 是否包含根路由, 即 '/'
 */
export function getMatchedRoutes(list: string[], includeRoot: boolean = false): RouteRecordRaw[] {
    const pathList = cloneDeep(list);
    const matched: RouteRecordRaw[] = [];
    let flatteningRoutes = usePermissionStore().flatteningRoutes;
    if (!includeRoot) flatteningRoutes = flatteningRoutes.filter(item => item.path !== "/");
    while (pathList.length > 0) {
        const item = flatteningRoutes.find(item => item.meta?.pathList?.join() === pathList.join());
        if (item) matched.push(item);
        pathList.pop();
    }
    return matched.reverse();
}

/**
 * 获取所有菜单中的第一个可访问菜单（顶级菜单）
 * 在隐藏首页时可定位到第一个可访问菜单
 */
export function getFirstMenuRoute(): RouteRecordRaw | undefined {
    const routes = filterNoPermissionTree(usePermissionStore().flatteningRoutes);
    const { VITE_HIDE_HOME } = import.meta.env;

    const judgeFirstRouteValid = (route: RouteRecordRaw | undefined): boolean => {
        if (!route) {
            return false; // 重定向路由不存在或被过滤
        } else if (route.path === "/" || route.path === "/welcome") {
            return false;
        } else if (route.meta?.showLink === false) {
            return false;
        } else if (route.redirect) {
            return judgeFirstRouteValid(routes.find(v => v.path === route.redirect));
        } else if (route.children && route.children.length > 0) {
            return false;
        }
        return true;
    };

    if (VITE_HIDE_HOME === "true") {
        return routes.find(v => judgeFirstRouteValid(v));
    } else {
        return routes.find(v => v.path === "/welcome");
    }
}

// #endregion-------------------------------------路由工具-------------------------------------------

// #region----------------------------------------初始化和重置路由-------------------------------------

/** 初始化路由（`new Promise` 写法防止在异步请求中造成无限循环） */
export function initRouter(): Promise<Router> {
    if (useConfigStore().GET_PLATFORM_CONFIG("CachingAsyncPermission")) {
        // 开启动态路由缓存本地localStorage
        const asyncRouteList = asyncPermissionStorage.value?.routes;
        const asyncPermissionsList = asyncPermissionStorage.value?.permissions;
        if (asyncRouteList && asyncRouteList?.length > 0) {
            return new Promise((resolve) => {
                // Note[vueuse] 踩坑, 直接操作useStorage的值会修改storage的值, 这里需要cloneDeep一份数据
                handleAsyncRoutes(cloneDeep(asyncRouteList));
                usePermissionStore().setPermissions(asyncPermissionsList || []);
                resolve(router);
            });
        } else {
            return new Promise((resolve, reject) => {
                authApi.findPermissions().then((resp) => {
                    if (ok(resp)) {
                        const { routes, permissions } = resp.data;
                        handleAsyncRoutes(cloneDeep(routes)); // cloneDeep一份数据，避免后面使用数据时修改了原数据
                        asyncPermissionStorage.value = { routes, permissions }; // 缓存到localStorage
                        usePermissionStore().setPermissions(permissions);

                        resolve(router);
                    } else {
                        ElMessage.error(resp.message);
                        reject(resp);
                    }
                }).catch((err) => {
                    reject(err);
                });
            });
        }
    } else {
        return new Promise((resolve, reject) => {
            authApi.findPermissions().then((resp) => {
                if (ok(resp)) {
                    const { routes, permissions } = resp.data;
                    handleAsyncRoutes(routes);
                    usePermissionStore().setPermissions(permissions);
                    resolve(router);
                } else {
                    ElMessage.error(resp.message);
                    reject(resp);
                }
            }).catch((err) => {
                reject(err);
            });
        });
    }
}

/** 重置路由 */
export function resetRouter(): void {
    router.clearRoutes();
    for (const route of initConstantRoutes) {
        router.addRoute(route);
    }
    router.options.routes = formatTwoStageRoutes(
        formatFlatteningRoutes(buildHierarchyTree(routeRankAscending(originRoutes.flat(Infinity)), "meta")),
    );
    usePermissionStore().clearAllCachePage();
}

// #endregion-------------------------------------初始化和重置路由-------------------------------------

// #region----------------------------------------处理嵌套路由----------------------------------------

/**
 * 将多级嵌套路由处理成一维数组，不改变原数组
 * @param routesList 传入路由
 * @returns 返回处理后的一维路由
 */
export function formatFlatteningRoutes(routesList: RouteRecordRaw[]): RouteRecordRaw[] {
    const copyList = cloneDeep(routesList); // cloneDeep一份数据，避免后面使用数据时修改了原数据
    if (copyList.length === 0) return copyList;
    for (let i = 0; i < copyList.length; i++) {
        const children = copyList[i].children;
        // 提示缺少层级标识, 处理成一维数组要保证层级关系, 防止后面处理时出错
        if (!copyList[i].meta?.pathList) {
            throw new Error(`请检查路由${copyList[i].path}是否未处理层级`);
        };
        if (children && children.length > 0) {
            // 下面操作改变了原数组, 下次循环时循环新的children, 不再需要递归
            // 同时子路由会放在父路由后面，不会被打散
            copyList.splice(i + 1, 0, ...children);
        }
    }
    return copyList;
}

/**
 * 一维数组处理成多级嵌套数组（三级及以上的路由全部拍成二级，keep-alive 只支持到二级缓存）
 * https://github.com/pure-admin/vue-pure-admin/issues/67
 * @param routesList 处理后的一维路由菜单数组
 * @returns 返回将一维数组重新处理成规定路由的格式，不改变原数组
 */
export function formatTwoStageRoutes(routesList: RouteRecordRaw[]): RouteRecordRaw[] {
    const homeRoute = routesList.find((v: RouteRecordRaw) => v.path === "/");
    if (routesList.length === 0 || !homeRoute) return routesList;
    const newRoutesList: RouteRecordRaw[] = [{
        component: homeRoute.component,
        name: homeRoute.name,
        path: homeRoute.path,
        redirect: homeRoute.redirect,
        meta: homeRoute.meta,
        children: routesList.filter((v: RouteRecordRaw) => v.path !== "/"),
    }];
    return newRoutesList;
}

// #endregion-------------------------------------处理嵌套路由----------------------------------------

// #region----------------------------------------处理后端路由----------------------------------------

/** 处理动态(后端)路由, 并合并到路由中 */
export function handleAsyncRoutes(routeList: RouteRecordRaw[]): void {
    if (routeList.length !== 0) {
        formatAsyncRoutes(routeList); // 处理后端路由
        usePermissionStore().setFlatteningRoutes(routeList); // 设置拍平后的路由
        // 下面代码为设置 router.options.routes[0].children (菜单路由), 层级和 usePermissionStore().flatteningRoutes 保持一致
        const flattenRoutes: RouteRecordRaw[] = toRaw(usePermissionStore().flatteningRoutes).filter(n => n.path !== "/");
        flattenRoutes.forEach((v) => {
            if (v.meta?.backstage) { // 是后端返回的路由
                router.options.routes[0].children?.push(v); // 将后端路由添加到 Home 的子路由中
                router.addRoute("Home", v); // 将后端路由挂载到 Home（Layout）名下，保证页面跟从 /layout/index.vue 的 router-view 渲染
            }
        });
        addNoMatchedRoute(); // 添加未匹配路由
    }
    usePermissionStore().setWholeMenus(routeList);

    // TODO: 处理多标签缓存
    //   if (!useMultiTagsStoreHook().getMultiTagsCache) {
    //     useMultiTagsStoreHook().handleTags("equal", [
    //       ...routerArrays,
    //       ...usePermissionStoreHook().flatteningRoutes.filter(
    //         v => v?.meta?.fixedTag
    //       )
    //     ]);
    //   }
    // addPathMatch();
}

/** 过滤后端传来的动态路由，对原数组进行更改，重新生成规范路由 */
function formatAsyncRoutes(arrRoutes: Array<RouteRecordRaw>): Array<RouteRecordRaw> {
    if (!arrRoutes || !arrRoutes.length) return arrRoutes;
    arrRoutes.forEach((v: RouteRecordRaw) => {
        // 将backstage属性加入meta，标识此路由为后端返回路由
        v.meta!.backstage = true;
        // 父级的redirect属性取值：如果子级存在且父级的redirect属性不存在，默认取第一个子级的path；如果子级存在且父级的redirect属性存在，取存在的redirect属性，会覆盖默认值
        if (v?.children && v.children.length && !v.redirect) v.redirect = v.children[0].path;
        if (!v.name) v.name = v.path.slice(1).replace(/\//g, "-"); // name 不存在时取 path，保证 name 存在，避免报错
        if (v.meta?.menuType === "iframe" && v.meta.url) {
            v.component = IFrame;
        } else if (v.component) {
            v.component = modulesRoutes[v.component as any];
        } else {
            /**
             * 对后端传component组件路径和不传做兼容
             * 如果后端传component组件路径, 那么path可以随便写, 如果不传，component组件路径会跟path保持一致
             */
            const vueFile = modulesRoutes[`/src/views${v.path}.vue`];
            const tsxFile = modulesRoutes[`/src/views${v.path}.tsx`];
            v.component = vueFile || tsxFile || null;
        }
        if (v?.children && v.children.length) {
            formatAsyncRoutes(v.children);
        }
    });
    return arrRoutes;
}

/** 添加未匹配路由, 在处理完后端路由之后添加, 避免刷新时后端路由还未加载直接跳转404页面 */
function addNoMatchedRoute(): void {
    if (!router.hasRoute("NotFound")) {
        router.addRoute({
            path: "/:pathMatch(.*)*", // 匹配所有未定义的路径
            name: "NotFound",
            redirect: "/error/404",
            meta: { title: "Not Found", showLink: false },
        });
    }
}

// #endregion-------------------------------------处理后端路由----------------------------------------

// #region----------------------------------------通过权限过滤菜单-------------------------------------

/** 自定义过滤菜单，返回新数组 */
export function filterNoPermissionTree(data: RouteRecordRaw[]): RouteRecordRaw[] {
    const newTree = cloneDeep(data);
    // ... 可以在这里添加更多的过滤条件
    return filterNoChildrenTree(newTree);
}

/** 过滤children长度为0的的目录，当目录下没有菜单时，会过滤此目录，当目录下只要有一个菜单有显示权限，那么此目录就会显示 */
function filterNoChildrenTree(data: RouteRecordRaw[]): RouteRecordRaw[] {
    const newTree = filterNoShowLinkTree(data); // 先过滤不展示的菜单
    return newTree.filter((v: any) => v?.children?.length !== 0);
}

/** 过滤meta中showLink为false的菜单 */
function filterNoShowLinkTree(data: RouteRecordRaw[]): RouteRecordRaw[] {
    const newTree = cloneDeep(data).filter(
        (v: RouteRecordRaw) => v.meta?.showLink !== false,
    );
    newTree.forEach(
        (v: RouteRecordRaw) => v.children && (v.children = filterNoShowLinkTree(v.children)),
    );
    return newTree;
}

// #endregion-------------------------------------通过权限过滤菜单-------------------------------------

// #region----------------------------------------处理缓存路由----------------------------------------

/** 处理缓存路由（添加、删除、刷新） */
export function handleAliveRoute({ name }: ToRouteType, mode?: string): void {
    switch (mode) {
        case "add":
            usePermissionStore().cacheOperate({
                mode: "add",
                name,
            });
            break;
        case "delete":
            usePermissionStore().cacheOperate({
                mode: "delete",
                name,
            });
            break;
        case "refresh":
            usePermissionStore().cacheOperate({
                mode: "refresh",
                name,
            });
            break;
        default:
            // TODO 为啥先删后加？
            usePermissionStore().cacheOperate({
                mode: "delete",
                name,
            });
            useTimeoutFn(() => {
                usePermissionStore().cacheOperate({
                    mode: "add",
                    name,
                });
            }, 100);
    }
}

// #endregion-------------------------------------处理缓存路由----------------------------------------
